package com.aphrodite.ui.widgets;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

import com.aphrodite.ui.schema.TableCell;
import com.aphrodite.util.ArrayUtil;
import com.aphrodite.util.StringUtil;

abstract class TabularItem extends TraversableWidget {

	/**
	 * blank cell , used at the end of reports. see
	 * {@link TabularItem#paddingRows}
	 */
	private static final TableCell blankCell = new TableCell("");
	/**
	 * table fonts
	 */
	private final static Font fntZoom = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE);
	/* Scroll Constants */
	private static final byte scroll_D = 1;
	private static final byte scroll_L = 3;
	private static final byte scroll_R = 4;
	private static final byte scroll_U = 0;

	/* Flag to enable disable drawing of grid lines */
	boolean canDrawGrid = true;
	private boolean canHscroll = true;

	private boolean canVscroll = true;
	/* X postion of a cell */
	short cellXpos;

	/* Y position of a cell */
	short cellYpos;
	// total number of columns that can be accommodated
	int colsCanAcc;
	/* width of each table column */
	short[] colWdth;
	/* current column location pointer (used by zoom) */
	private short currCol;
	/* current row location pointer (used by zoom) */
	private short currRow = 0;// 1;
	// flag to zoom into a cell
	boolean doCellZoom;

	// the ending row
	private int endRow;

	/* current column that is frozen */
	private short frozenCol = 1;

	/* current row that is frozen */
	private short frozenRow = 0;

	private boolean isColFrozen;

	private boolean isRowFrozen;

	/* space between each line in a cell */
	byte lineSpacing = 2;// 15;

	/* Number of row to use as padding at the end of the table */
	byte paddingRows = 3;// 2

	/* Present row color when row freeze is applied */
	// private int[] presentcolor;
	/* the height of each row */
	short[] rowHeight;

	short rowsCanAcc;
	/* Scroll bars */
	ScrollBar sBarH;

	ScrollBar sBarV;

	private int selcol;

	/* Color of the selection rectangle */
	// int selectorColor = 0x000000;
	/* column that is selected by default */
	short selectedCol;

	/* The row over which the selection rectangle is standing */
	short selectedRow = 0;
	/* the col to start scrolling from */
	private int startCol;
	private int startRow = 0;// 1;

	TableCell[][] tableData;

	/* Width of the temporary columns */
	short[] tempColWdth;
	short[] tempRowHt;
	// used to store the list of rows currently displayed
	TableCell[][] tempTblData;
	/* space between the cell and start of the text */
	byte textLeftPad = 4;// 2;
	/* padding when text is right indented */
	byte textRtPad = 2;
	int totalCols;
	int totalRows;

	TabularItem(Theme theme, TableCell[][] cells, short[] columnWidth, int width, int height) {
		super(theme, width, height);
		setData(cells, columnWidth);
	}

	/*
	 * columns the screen can accomodate
	 */
	private final void calcColsCanAcc() {
		if (!canHscroll) {
			colsCanAcc = colWdth.length - 1;
		}
		int temp = 0;
		boolean foundNull = false;
		for (int i = 0; i < tempColWdth.length; i++) {
			// System.out.println("current width: " + tmpwidth[i]);
			if (tempColWdth[i] == 0)
				foundNull = true;
			if (temp >= width) {
				// System.out.println("Cols can accomodate are con1: " + i);
				colsCanAcc = !foundNull ? i : i - 1;
				break;
			}
			temp += tempColWdth[i];
		}
	}

	// /**
	// * calculate the number of rows the table can occupy within the given
	// * height.
	// *
	// * @return number of rows
	// */
	// private final int calcEndRow2() {
	//
	// return number;
	// }

	abstract void calcRowHeight();

	/*
	 * rows the screen can accomodate
	 */
	private final void calcRowsCanAcc() {
		if (!canVscroll) {
			rowsCanAcc = (short) (totalRows - 1);
		} else {
			rowsCanAcc = (short) ((tempTblData.length > 10) ? 10 : tempTblData.length - 1);// 10
			// ;
			int temp = 0;
			int tableHeight = height - (sBarH.getHeight() + titleHeight);
			for (short i = 0; i < tempRowHt.length; i++) {
				temp += tempRowHt[i];
				if (temp >= tableHeight) {
					rowsCanAcc = i; // i;
					break;
				} else if (temp == tableHeight) {

				}
			}
		}
	}

	public boolean canDrawGrid() {
		return canDrawGrid;
	}

	final void doCellZoom(final Graphics g) {
		String content = null;
		int cellColor = 0;
		int fontColor = 0;
		try {
			TableCell curr = getSelectedCell();
			content = curr.cellCaption;
			cellColor = curr.cellColor;
			fontColor = curr.fontColor;
		} catch (ArrayIndexOutOfBoundsException e) {
			content = "";
			cellColor = 0xffffff;
			fontColor = 0x000000;
		}
		/* 2=left pad */
		int zoomWidth = width - sBarV.getWidth() - 2;
		String[] lines = StringUtil.doTextWrap(content, fntZoom, zoomWidth, width - 2);
		int boxWidth = zoomWidth;
		int boxHeight = 4 + (fntZoom.getHeight() * lines.length - 1);
		int boxX = 2;
		int boxY = (height / 2) - (boxHeight / 2);
		g.setColor(cellColor);
		g.fillRect(boxX, boxY, boxWidth - 2, boxHeight);
		g.setColor(0x000000);
		g.drawRect(boxX, boxY, boxWidth - 2, boxHeight);
		g.setColor(fontColor);
		g.setFont(fntZoom);
		for (int i = 0; i < lines.length; i++) {
			g.drawString(lines[i], boxX, 2 + boxY + fntZoom.getHeight() * i, 0);
		}
	}

	/* draw vertical grid lines */
	final void drawCols(final Graphics g) {
		if (!canDrawGrid)
			return;
		g.setColor(theme.borderColor);
		int startPoint;
		// constant and a factor of distance from the start point
		for (int i = 0; i < colsCanAcc + 1; i++) {
			startPoint = getCellXPosition(i);
			g.drawLine(startPoint, 0, startPoint, height - 1);
		}
	}

	public final void fire() {
		zoomCell();
		super.fire();
	}

	/**
	 * 
	 * @param column
	 */
	public void freezeColumn(short column) {
		isColFrozen = true;
		this.frozenCol = column;
		selcol = selectedCol;
		// setColumnFreezeColor();
		reDrawWidget();
	}

	/**
	 * 
	 * @param row
	 *            to be frozen
	 */
	public void freezeRow(short row) {
		this.isRowFrozen = true;
		frozenRow = row;
		reDrawWidget();
	}

	final short getCellXPosition(final int index) {
		return (index == 0) ? 0 : (short) ArrayUtil.getSum(tempColWdth, 0, index);
	}

	short getCellYPosition(int index) {
		short ret = 0;
		switch (index)// for 7
		{
		case 0:
			ret = 0;
			break;
		case 1:
			ret = tempRowHt[0];
			break;
		default:
			return (short) ArrayUtil.getSum(tempRowHt, 0, index - 1);
			// for (int i = 0; i <= index - 1; i++) {
			// ret += tempRowHt[i];
			// }
			// break;
		}
		return ret;
	}

	/*
	 * returns the current column
	 */
	public final short getCurrentColumn() {
		return currCol;
	}

	/*
	 * returns the current row
	 */
	public final int getCurrentRow() {
		return currRow;
	}

	/**
	 * returns the index of the cell with the longest text
	 * 
	 * @param row
	 *            a table row
	 * @return int
	 */
	int getLongestCellIndex(final TableCell[] row) {
		int longest = row[0].cellCaption.length();// set the longest cell to
		// the first cell in the row
		int curr = 0;
		int index = 0;
		for (int i = 0; i < row.length; i++) {
			curr = row[i].cellCaption.length();
			// longest = Math.max(curr, longest);
			// index = i;
			if (curr > longest) {
				longest = curr;
				index = i;
			}
		}
		return index;
	}

	public TableCell getSelectedCell() {
		if (tempTblData[selectedRow][selectedCol] != null) {
			if (tempTblData[selectedRow][selectedCol].columnSpan != 0) {
				return tempTblData[selectedRow][selectedCol];
			} else {/* Get the cell if the selected cell is in column span */
				int col = selectedCol;
				for (; tempTblData[selectedRow][col].columnSpan == 0;) {
					col--;
				}
				return (tempTblData[selectedRow][col] != null) ? tempTblData[selectedRow][col] : blankCell;
			}
		}
		return blankCell;
	}

	public final TableCell[][] getTableData() {
		return tableData;
	}

	int getTextIndent(final int column, final TableCell cell, final int textWidth) {
		if (cell.columnSpan > 1) {
			int tmpW = cellXpos + tempColWdth[column];
			int colSpan = cell.columnSpan - 1;
			int i = 0;
			for (int j = colSpan; j > 0; j--) {
				i++;
				tmpW += getCellXPosition(column + i);
				// cs--;
			}

			/*
			 * if text spans across cells that are beyond the width of the
			 * table, left indent the text
			 */
			// return (tmpW > width) ? cellXpos + textLeftPadding : (tmpW / 2) -
			// (textWidth / 2);
			int textX = (tmpW / 2) - (textWidth / 2);
			return (textX > width) ? cellXpos + textLeftPad : textX;

		} else {
			switch (cell.textIndent) {
			case 3:// right indent
				return cellXpos + (tempColWdth[column] - textWidth - textRtPad);// 2
			case 2:// center indent
				return cellXpos + (tempColWdth[column] / 2 - textWidth / 2);
			default:// left indent
				return cellXpos + textLeftPad;
			}
		}
	}

	/*
	 * used to initialise the temporary arrays used in the drawing process
	 */
	private final void initArrays() {
		tempTblData = new TableCell[totalRows][totalCols];
		tempRowHt = new short[totalRows];
		tempColWdth = new short[totalCols + 1];
	}

	public boolean isRowFrozen() {
		return isRowFrozen;
	}

	public final boolean moveDown() {
		// System.out.println("[Table.moveDown]");
		if (selectedRow < rowsCanAcc - 1) {
			selectedRow++;
			currRow++;
		} else {
			if (endRow < totalRows) {
				if (canVscroll)
					scroll(scroll_D);
			}
		}
		setScrollPct();
		reDrawWidget();
		return true;
	}

	public final boolean moveLeft() {
		if (selectedCol > 0) {
			selectedCol--;
			currCol--;
		} else {// if (selectedCol == 0) {
			if (startCol != 0) {
				scroll(scroll_L);
			}
		}
		setScrollPct();
		reDrawWidget();
		return true;
	}

	public final boolean moveRight() {
		// if (selectedCol < colsCanAcc - 1) {
		if (selectedCol < colsCanAcc) {
			selectedCol++;
			currCol++;
		} else {
			/*
			 * uncomment to have extra space during column scroll. but using
			 * those extra columns will throw a array index exception if used
			 */
			// if (selectedCol == colsCanAccomodate - 1) {
			if (startCol < totalCols - colsCanAcc && canHscroll) {
				// if (canHscroll)/* scroll only if it can be performed */
				scroll(scroll_R);
			}
		}
		setScrollPct();
		reDrawWidget();
		return true;
	}

	public final boolean moveUp() {
		// System.out.println("[Table.moveUP]");
		// TODO change to 1 if full scroll fails
		if (selectedRow > ((isRowFrozen) ? 1 : 0)) {
			selectedRow--;
			currRow--;
		} else if (selectedRow == ((isRowFrozen) ? 1 : 0)) {
			// if (startRow != ((rowFreeze) ? 1 : 0)) { {
			if (startRow != ((isRowFrozen) ? 1 : 0)) {
				// /* scroll up */
				if (startRow != 0)
					scroll(scroll_U);
			}
		}
		setScrollPct();
		reDrawWidget();
		return true;
	}

	/*
	 * paint the cell color. if the cell is column spanned then paint all the
	 * spanned cells
	 */
	final void paintCellColor(final Graphics g, final int row, final int col) {
		g.setColor(tempTblData[row][col].cellColor);
		if (tempTblData[row][col].columnSpan > 1) {
			int tmpWidth = tempColWdth[col];
			int colspan = tempTblData[row][col].columnSpan - 1;
			int i = 0;
			for (int j = colspan; j > 0; j--) {
				i++;
				tmpWidth += tempColWdth[col + i];
			}
			g.fillRect(cellXpos + 1, cellYpos + 1, tmpWidth - 1, tempRowHt[row] - 1);
		} else {
			g.fillRect(cellXpos + 1, cellYpos + 1, tempColWdth[col] - 1, tempRowHt[row] - 1);
		}
	}

	public final void removeColumnFreeze() {
		isColFrozen = false;
		// for (int i = 0; i < totalRows - paddingRows; i++) {// -1
		// tableData[i][frozenCol].cellColor = presentcolor[i];
		// }
		// presentcolor = null;
		// reDrawWidget();

	}

	/**
	 * Adjust the table height to occupy all the rows
	 */
	public final void resizeToFit() {
		height = ArrayUtil.getSum(rowHeight, 0, rowHeight.length);
		width = ArrayUtil.getSum(colWdth, 0, colWdth.length);
		reDrawWidget();
	}

	/*
	 * function used in scrolling
	 */
	private void scroll(final byte direction) {
		switch (direction) {
		case scroll_U:
			startRow--;
			endRow--;
			currRow--;
			break;
		case scroll_D:
			startRow++;
			endRow++;
			currRow++;
			break;
		case scroll_L:
			startCol--;
			currCol--;
			break;
		case scroll_R:
			startCol++;
			currCol++;
			break;
		}
		// TODO uncomment remove full scroll
		// copy the first row height
		if (isRowFrozen) {
			tempRowHt[0] = rowHeight[frozenRow];
		}
		int colStartIndex = startCol; // the current col to start reading from
		int currentRow = 0; // here it marks the current col
		int i = 0;
		// TODO uncomment to remove full scroll
		if (isRowFrozen) {
			// copy the first row
			for (i = colStartIndex; i < totalCols; i++) {
				tempTblData[0][currentRow] = tableData[frozenRow][i];
				currentRow++;
			}
		}

		// copy all the column width
		for (int j = 0; j < totalCols; j++) {
			if (colStartIndex < colWdth.length) {
				tempColWdth[j] = colWdth[colStartIndex];
				colStartIndex++;
			} else
				break;
		}

		// copy the cells as per the scroll
		colStartIndex = startCol;
		// TODO uncomment to remove Full scroll
		currentRow = isRowFrozen ? 1 : 0;
		// System.out.println("Start Row Value " + startRow);
		for (i = startRow; i < endRow; i++) {
			// for scrolling through the cols
			for (int j = 0; colStartIndex < totalCols; j++) {
				if (isColFrozen) {// skip copying the frozen column
					if (colStartIndex == frozenCol) {
						tempColWdth[j] = 0;// uncomment if you dont want the
					}
					// else
					// tempTableData[currentRow][j] = getCell(i, colStartIndex);
				}
				// copy the current row into the temp row container
				tempTblData[currentRow][j] = tableData[i][colStartIndex];
				if (colStartIndex <= totalCols) {
					colStartIndex++;
				}
			}
			// assign the row heights
			tempRowHt[currentRow] = rowHeight[i];
			if (isRowFrozen) {
				tempRowHt[currentRow] = (frozenRow == i) ? 0 : rowHeight[i];
			}
			// reset the index of the current col to the starting col
			colStartIndex = startCol;
			// current row increment
			currentRow++;
		}

		if (direction < 2) {/* scroll_U || scroll_D */
			calcRowsCanAcc();
		}
		/* do column freeze if enabled */
		if (isColFrozen) {
			if (isRowFrozen)
				tempTblData[0][selcol] = tableData[frozenRow][frozenCol];
			tempColWdth[selcol] = colWdth[frozenCol];
			// tmpwidth[freezeColumn] = twidth[freezeColumn];
			int current = isRowFrozen ? 1 : 0;// 0 : 1;
			for (i = startRow; i < endRow; i++) {
				tempTblData[current][selcol] = tableData[i][frozenCol];
				current++;
			}
		}
		if (direction > 2) {/* scroll_L || scroll_R */
			calcColsCanAcc();
		}
	}

	public final void setColumnFreeze(short column) {
		isColFrozen = true;
		this.frozenCol = column;
		selcol = selectedCol;
		// setColumnFreezeColor();
		reDrawWidget();
	}

	// private void setColumnFreezeColor() {
	// presentcolor = new int[totalRows - paddingRows]; // -1
	// // TODO remove padding
	// for (int i = 0; i < totalRows - paddingRows; i++) { // -1
	// presentcolor[i] = tableData[i][frozenCol].cellColor;
	// tableData[i][frozenCol].cellColor = 0xd5d5d5;
	// }
	// }

	/**
	 * set the table data
	 * 
	 * @param _data
	 * @param columnWidth
	 *            if null default column width is used
	 */
	public final void setData(TableCell[][] _data, short[] columnWidth) {
		if (columnWidth != null) {
			this.colWdth = columnWidth;// schema.getColWidth();
		} else {
			this.colWdth = new short[_data[0].length];
			for (int i = 0; i < colWdth.length; i++) {
				colWdth[i] = 80;
			}
		}
		setScrollBars();
		// Adding padding cells at the right and bottom end of the table

		// TODO remove padding
		this.tableData = new TableCell[_data.length + paddingRows][_data[0].length + 1];
		for (int i = 0; i < _data.length + 1; i++) {
			for (int j = 0; j < _data[0].length + 1; j++) {
				try {
					this.tableData[i][j] = (_data[i][j] != null) ? _data[i][j] : blankCell;
				} catch (ArrayIndexOutOfBoundsException e) {
					this.tableData[i][j] = blankCell;
				}
			}
		}
		// add an addition column width for the column just added
		colWdth = ArrayUtil.append(colWdth, new short[] { 80 });
		this.totalRows = tableData.length;
		this.totalCols = tableData[0].length;
		initArrays();
		calcRowHeight();
		/* Check if scrolling is needed */
		if (ArrayUtil.getSum(columnWidth, 0, columnWidth.length) < width) {
			canHscroll = false;
		}
		if (ArrayUtil.getSum(rowHeight, 0, rowHeight.length) < height) {
			canVscroll = false;
		}
		/* calculate the end row for drawing */
		int temp = 0;
		for (int i = 0; i < rowHeight.length; i++) {
			temp += rowHeight[i];
			if (temp > height) {
				endRow = i;
				break;
			}
		}

		// calulateEndRowOnInit();

		// TODO set to 1 to remove set row freeze
		startRow = isRowFrozen ? 1 : 0;
		startCol = 0;
		// TODO set to 1 to remove set row freeze
		currRow = (short) (isRowFrozen ? 1 : 0);// 1;
		currCol = 0;
		selectedCol = 0;
		selectedRow = (short) (isRowFrozen ? 1 : 0);
		scroll(scroll_D);
		scroll(scroll_U);
		calcColsCanAcc();
		reDrawWidget();
		// calcRowsCanAccomodate();
	}

	public void setDrawGrid(boolean canDrawGrid) {
		this.canDrawGrid = canDrawGrid;
	}

	/**
	 * Spacing between each line in a cell
	 * 
	 * @param spacing
	 *            a value within 128
	 */
	public final void setLineSpacing(final byte spacing) {
		this.lineSpacing = spacing;
		calcRowHeight();
		moveDown();
		moveUp();
	}

	public void setRowFreeze(boolean freeze) {
		this.isRowFrozen = freeze;
		// TODO remove if doesnt work
		frozenRow = currRow;
		// if (freeze) {
		// currentRow = 1;
		// }
		reDrawWidget();
	}

	private void setScrollBars() {
		this.sBarV = new ScrollBar();
		this.sBarV.setX(width - 7);
		this.sBarV.setY(0);
		this.sBarV.setBarDimensions(7, height);
		this.sBarV.setLayout(Widget.LAYOUT_VERTICAL);
		this.sBarH = new ScrollBar();
		this.sBarH.setX(0);
		this.sBarH.setY(height - 7);
		sBarH.setBarDimensions(7, width - 7);
	}

	private final void setScrollPct() {
		sBarV.setPercent((currRow * 100) / totalRows);
		sBarH.setPercent((currCol * 100) / totalCols);
	}

	/**
	 * space between left side of the cell and the text.Default is 4.
	 * 
	 * @param pad
	 */
	public final void setTextLeftPadding(final byte pad) {
		textLeftPad = pad;
		// calcRowHeight();
		moveDown();
		moveUp();
	}

	/**
	 * Space between the right of the cell and the text(on right indent).Default
	 * is 2px
	 * 
	 * @param pad
	 */
	public final void setTextRightPadding(final byte pad) {
		textRtPad = pad;
		moveDown();
		moveUp();
	}

	public void setTitle(String title) {
		super.setTitle(title);
		setScrollBars();
		reDrawWidget();
	}

	protected boolean traverse(int dir, int viewportWidth, int viewportHeight, int[] visRect_inout) {
		return false;
	}

	public void zoomCell() {
		doCellZoom = (doCellZoom) ? false : true;
		reDrawWidget();
	}
}
